#include <iostream>
#include <vector>
#include <string>
#include <fstream>
#include <sys/stat.h>
#include <unordered_map>

static size_t Spilt(const std::string& src,const std::string& sep,std::vector<std::string>* arr)
    {
        //"abc,sdf,ijk"  ","
        int offset = 0; //偏移量
        while(offset < src.size())
        {
            int pos = src.find(sep,offset);
            if(pos == std::string::npos)
            {
                //没找到，说明后面的offset后面的字符都是结果
                arr->push_back(src.substr(offset,pos-offset));
                return arr->size();
            }
            //这里可能有多个重复sep
            if(offset == pos){
                offset = pos + sep.size();
                continue;
            }
            //收集结果
            arr->push_back(src.substr(offset,pos-offset));
            offset = pos + sep.size();
        }
        return arr->size();
}

static bool ReadFile(const std::string& filename,std::string* buf)
{
    std::ifstream ifs(filename,std::ios::binary);
    if(!ifs.is_open())
    {
        printf("OPEN %s FAILED!",filename);
        return false;
    }
    int fsize = 0;
    //先把文件指针偏移到末尾，获取到文件的大小
    ifs.seekg(0,ifs.end);
    fsize = ifs.tellg();
    //回到起始，开始读取文件
    ifs.seekg(0,ifs.beg);
    buf->resize(fsize);
    //读取文件
    ifs.read(&(*buf)[0],fsize);
    if(!ifs.good())
    {
        printf("READ FILE %s FAILED!",filename);
        ifs.close();
        return false;
    }
    //记得关闭文件，防止资源泄露
    ifs.close();
    return true;
}



static bool WriteFile(const std::string& filename,const std::string buf)
{
    std::ofstream ofs(filename,std::ios::binary | std::ios::trunc);
    if(!ofs.is_open())
    {
        printf("OPEN %s FAILED!",filename.c_str());
        return false;
    }
    //向文件写入
    ofs.write(buf.c_str(),buf.size());
    if(!ofs.good())
    {
        printf("WRITEFILE %s FAILED!",filename.c_str());
        ofs.close();
        return false;
    }
    ofs.close();
    return true;
}

 static std::string UrlEnCode(const std::string& url,bool convert_space_to_plus)
{
    //". - _ ~"以及数字字母字符采用绝对不编码，convert_space_to_plus -> 是否启用空格转+
    std::string ret;
    for(auto ch: url)
    {
        if(ch == '.' || ch == '-' || ch == '_' || ch == '~' || isalnum(ch))
        {
            ret += ch;
            continue;
        }
        //空格转+
        if(convert_space_to_plus && ch == ' ')
        {
            ret += '+';
            continue;
        }
        //其余都是需要转换的字符
        char tmp[4] = {0};
        snprintf(tmp,4,"%%%02X",ch);
        ret += tmp;
    }
    return ret;
}

static char HexToI(char ch)
{
    if(ch >= '0' && ch <= '9')
    {
        return ch - '0';
    }
    else if(ch >= 'a' && ch <= 'z')
    {
        return ch - 'a' + 10;
    }
    else if(ch >= 'A' && ch <= 'Z')
    {
        return ch - 'A' + 10;
    }
    return -1; //错误
}
//url解码
static std::string UrlDeCode(const std::string& url,bool convert_plus_to_space)
{
    std::string ret;
    for(int i = 0;i<url.size();++i)
    {
        //判断convert_plus_to_space条件是否满足
        if(convert_plus_to_space && url[i] == '+')
        {
            ret += ' ';
            continue;
        }
        //遇到%后面的数，就把第一个数转换成16进制第二个数相加
        if(url[i] == '%')
        {
            char v1 = HexToI(url[i+1]);
            char v2 = HexToI(url[i+2]);
            char res = (v1 << 4) + v2;   
            ret +=  res;
            i += 2;
            continue;
        }
        //其他情况直接放入结果集即可
        ret += url[i];
    }
    return ret;
}



static std::string StatuDesc(int statu)
{
    std::unordered_map<int,std::string> stu_msg = {
        {100, "Continue"},
        {101, "Switching Protocol"},
        {102, "Processing"},
        {103, "Early Hints"},
        {200, "OK"},
        {201, "Created"},
        {202, "Accepted"},
        {203, "Non-Authoritative Information"},
        {204, "No Content"},
        {205, "Reset Content"},
        {206, "Partial Content"},
        {207, "Multi-Status"},
        {208, "Already Reported"},
        {226, "IM Used"},
        {300, "Multiple Choice"},
        {301, "Moved Permanently"},
        {302, "Found"},
        {303, "See Other"},
        {304, "Not Modified"},
        {305, "Use Proxy"},
        {306, "unused"},
        {307, "Temporary Redirect"},
        {308, "Permanent Redirect"},
        {400, "Bad Request"},
        {401, "Unauthorized"},
        {402, "Payment Required"},
        {403, "Forbidden"},
        {404, "Not Found"},
        {405, "Method Not Allowed"},
        {406, "Not Acceptable"},
        {407, "Proxy Authentication Required"},
        {408, "Request Timeout"},
        {409, "Conflict"},
        {410, "Gone"},
        {411, "Length Required"},
        {412, "Precondition Failed"},
        {413, "Payload Too Large"},
        {414, "URI Too Long"},
        {415, "Unsupported Media Type"},
        {416, "Range Not Satisfiable"},
        {417, "Expectation Failed"},
        {418, "I'm a teapot"},
        {421, "Misdirected Request"},
        {422, "Unprocessable Entity"},
        {423, "Locked"},
        {424, "Failed Dependency"},
        {425, "Too Early"},
        {426, "Upgrade Required"},
        {428, "Precondition Required"},
        {429, "Too Many Requests"},
        {431, "Request Header Fields Too Large"},
        {451, "Unavailable For Legal Reasons"},
        {501, "Not Implemented"},
        {502, "Bad Gateway"},
        {503, "Service Unavailable"},
        {504, "Gateway Timeout"},
        {505, "HTTP Version Not Supported"},
        {506, "Variant Also Negotiates"},
        {507, "Insufficient Storage"},
        {508, "Loop Detected"},
        {510, "Not Extended"},
        {511, "Network Authentication Required"}
    };
    auto it = stu_msg.find(statu);
    if(it != stu_msg.end())
    {
        //找到了
        return it->second;
    }
    return "UnKown";
}
//通过文件后缀名获得mime
static std::string GetMime(const std::string& filename)
{
    std::unordered_map<std::string,std::string> mim_msg = {
        {".aac", "audio/aac"},
        {".abw", "application/x-abiword"},
        {".arc", "application/x-freearc"},
        {".avi", "video/x-msvideo"},
        {".azw", "application/vnd.amazon.ebook"},
        {".bin", "application/octet-stream"},
        {".bmp", "image/bmp"},
        {".bz", "application/x-bzip"},
        {".bz2", "application/x-bzip2"},
        {".csh", "application/x-csh"},
        {".css", "text/css"},
        {".csv", "text/csv"},
        {".doc", "application/msword"},
        {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
        {".eot", "application/vnd.ms-fontobject"},
        {".epub", "application/epub+zip"},
        {".gif", "image/gif"},
        {".htm", "text/html"},
        {".html", "text/html"},
        {".ico", "image/vnd.microsoft.icon"},
        {".ics", "text/calendar"},
        {".jar", "application/java-archive"},
        {".jpeg", "image/jpeg"},
        {".jpg", "image/jpeg"},
        {".js", "text/javascript"},
        {".json", "application/json"},
        {".jsonld", "application/ld+json"},
        {".mid", "audio/midi"},
        {".midi", "audio/x-midi"},
        {".mjs", "text/javascript"},
        {".mp3", "audio/mpeg"},
        {".mpeg", "video/mpeg"},
        {".mpkg", "application/vnd.apple.installer+xml"},
        {".odp", "application/vnd.oasis.opendocument.presentation"},
        {".ods", "application/vnd.oasis.opendocument.spreadsheet"},
        {".odt", "application/vnd.oasis.opendocument.text"},
        {".oga", "audio/ogg"},
        {".ogv", "video/ogg"},
        {".ogx", "application/ogg"},
        {".otf", "font/otf"},
        {".png", "image/png"},
        {".pdf", "application/pdf"},
        {".ppt", "application/vnd.ms-powerpoint"},
        {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"},
        {".rar", "application/x-rar-compressed"},
        {".rtf", "application/rtf"},
        {".sh", "application/x-sh"},
        {".svg", "image/svg+xml"},
        {".swf", "application/x-shockwave-flash"},
        {".tar", "application/x-tar"},
        {".tif", "image/tiff"},
        {".tiff", "image/tiff"},
        {".ttf", "font/ttf"},
        {".txt", "text/plain"},
        {".vsd", "application/vnd.visio"},
        {".wav", "audio/wav"},
        {".weba", "audio/webm"},
        {".webm", "video/webm"},
        {".webp", "image/webp"},
        {".woff", "font/woff"},
        {".woff2", "font/woff2"},
        {".xhtml", "application/xhtml+xml"},
        {".xls", "application/vnd.ms-excel"},
        {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
        {".xml", "application/xml"},
        {".xul", "application/vnd.mozilla.xul+xml"},
        {".zip", "application/zip"},
        {".3gp", "video/3gpp"},
        {".3g2", "video/3gpp2"},
        {".7z", "application/x-7z-compressed"}
    };
    //a.txt 找到文件后缀
    size_t pos = filename.find_last_of('.');
    if(pos == std::string::npos)
    {
        //没找到就是二进制流数据
        return "application/octet-stream";
    }
    std::string tmp = filename.substr(pos);
    auto it = mim_msg.find(tmp);
    if(it == mim_msg.end())
    {
        return "application/octet-stream";
    }
    return it->second;
}

static bool IsDir(const std::string& filename)
{
    struct stat st;
    int ret = stat(filename.c_str(),&st);
    if(ret < 0)
    {
        return false;
    }
    return S_ISDIR(st.st_mode);
}
//判断一个文件是否是普通文件
static bool IsRegular(const std::string& filename)
{
    struct stat st;
    int ret = stat(filename.c_str(),&st);
    if(ret < 0)
    {
        return false;
    }
    return S_ISREG(st.st_mode);
}
//资源路径的有效性判断
static bool IsValPath(const std::string& path)
{
    //根据层数来判断当前路径是否在web根目录下
    //"/index.html"  "/../index.html"
    std::vector<std::string> vs;
    Spilt(path,"/",&vs);
    int level = 0;
    for(auto& str:vs)
    {
        if(str == "..")
        {
            --level;
            if(level < 0) return false; //任意一层小于0都是有问题的
            continue;
        }
        ++level;
    }
    return true;
}

int main()
{
    std::cout<<IsValPath("./index.html")<<std::endl;
    std::cout<<IsValPath("/index.html/../../a.html")<<std::endl;

    // std::cout<<IsDir("../example")<<std::endl;
    // std::cout<<IsDir("./test.cc")<<std::endl;
    // std::cout<<IsRegular("../example")<<std::endl;
    // std::cout<<IsRegular("./test.cc")<<std::endl;

    // std::cout<<StatuDesc(302)<<std::endl;
    // std::cout<<GetMime("a.txt")<<std::endl;
    // std::string str = "C  ";
    // std::string ret = UrlEnCode(str,true);
    // std::string tmp = UrlDeCode(ret,true);
    // std::cout<<ret<<std::endl;
    // std::cout<<tmp<<std::endl;

    // std::string filename = "./bind.cc";
    // std::string buf;
    // ReadFile(filename,&buf);

    // WriteFile("./tmp",buf);

    // std::vector<std::string> arr;
    // std::string src = "abc,,,,,def,ijk,,";
    // std::string sep = ",";
    // Spilt(src,sep,&arr);
    // for(auto& s:arr)
    // {
    //     std::cout<<s<<std::endl;
    // }
    return 0;
}